package com.tjbklx1.redis.chap01;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.Test;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.ZParams;

/**
 * chap1.3 案例,根据文章的发布时间,获得的投票数量进行评分
 * @author tjbklx1
 * @date 2018-12-1
 */
public class App02 {

	private static final int ONE_WEEK_IN_SECONDS = 7 * 86400;
	// 一天最多得200张票,即满分设定为是 86400,86400/200=432,所以每获得一张票得分 432
	private static final int VOTE_SCORE = 432;
	private static final int ARTICLES_PER_PAGE = 25;

	@Test
	public void process() {

		Jedis conn = new Jedis("localhost");
		conn.select(0);

		//发布文章
		String articleId = postArticle(conn, "username", "A title", "http://www.google.com");
		System.out.println("We posted a new article with id: " + articleId);

		System.out.println("Its HASH looks like:");
		// 查询文章的信息 HASH查询所有元素
		Map<String, String> articleData = conn.hgetAll("article:" + articleId);
		// HASH的对象对应Java Map ,便利Map对象
		for (Map.Entry<String, String> entry : articleData.entrySet()) {
			System.out.println("\t\t" + entry.getKey() + ": " + entry.getValue());
		}

		System.out.println("===========================");

		articleVote(conn, "other_user", "article:" + articleId);
		// 从文章的HASH对象中 获取投票计数器
		String votes = conn.hget("article:" + articleId, "votes");
		System.out.println("We voted for the article, it now has votes: " + votes);
		assert Integer.parseInt(votes) > 1;

		
		
//		System.out.println("The currently highest-scoring articles are:");
//		//查询积分最高的第一页文章
//		List<Map<String, String>> articles = getArticles(conn, 1);
//		printArticles(articles);
//		assert articles.size() >= 1;
//
//		
//		
//		//群组功能
//		addGroups(conn, articleId, new String[] { "new-group" });
//		System.out.println("We added the article to a new group, other articles include:");
//		articles = getGroupArticles(conn, "new-group", 1);
//		printArticles(articles);
//		assert articles.size() >= 1;

		conn.close();
	}

	/**
	 * 发布文章
	 * 
	 * @param conn
	 *            链接
	 * @param user
	 *            用户
	 * @param title
	 *            标题
	 * @param link
	 *            链接
	 * @return
	 * @date 2018年9月6日
	 */
	public String postArticle(Jedis conn, String user, String title, String link) {
		String articleId = String.valueOf(conn.incr("article:")); // 文章列表计数器

		// 文章投票KEY
		String voted = "voted:" + articleId;
		// 文章投票的数据类型为SET, <voted:ID ,USER>,为文章投票,即SADD
		conn.sadd(voted, user);  //为自己投票么?
		// 失效时间为1周
		conn.expire(voted, ONE_WEEK_IN_SECONDS);

		long now = System.currentTimeMillis() / 1000;
		// 文章的key
		String article = "article:" + articleId;
		// 组织文章内容
		HashMap<String, String> articleData = new HashMap<String, String>();
		articleData.put("title", title);
		articleData.put("link", link);
		articleData.put("user", user);
		articleData.put("now", String.valueOf(now));
		articleData.put("votes", "1"); // 投票计数器
		// 文章的内容格式是HASH <article:ID , Obj>
		// 增加文章的内容,对文章的key设置hash对象
		conn.hmset(article, articleData);
		// 分数 ,数据类型 有序集合
		// 增加文章的分数, 分数值,对象为文章KEY
		conn.zadd("score:", now + VOTE_SCORE, article); // 按照积分来展示
		conn.zadd("time:", now, article); // 按照发布时间来展示

		return articleId;
	}

	/**
	 * 记录文章记录投票用户
	 * 
	 * @param conn
	 * @param user
	 * @param article
	 * @date 2018年9月6日
	 */
	public void articleVote(Jedis conn, String user, String article) {
		long cutoff = (System.currentTimeMillis() / 1000) - ONE_WEEK_IN_SECONDS;
		// 文章发布一周之后,不能再为它投票
		if (conn.zscore("time:", article) < cutoff) {
			return;
		}

		// 获取文章ID
		String articleId = article.substring(article.indexOf(':') + 1);
		// 投票成功
		if (conn.sadd("voted:" + articleId, user) == 1) {
			// 积分加432
			conn.zincrby("score:", VOTE_SCORE, article);
			// 文章的内容 投票计数器+1
			conn.hincrBy(article, "votes", 1l);
		}
	}

	/**
	 * 根据score排序 ,选择积分最高的几篇文章
	 * 
	 * @param conn
	 * @param page
	 *            页码
	 * @return
	 * @date 2018年9月6日
	 */
	public List<Map<String, String>> getArticles(Jedis conn, int page) {
		return getArticles(conn, page, "score:");
	}

	/**
	 * 根order字段排序,分页查询最高的第几页的文章
	 * 
	 * @param conn
	 * @param page
	 *            页码
	 * @param order
	 *            排序字段
	 * @return
	 * @date 2018年9月6日
	 */
	public List<Map<String, String>> getArticles(Jedis conn, int page, String order) {
		int start = (page - 1) * ARTICLES_PER_PAGE; // 起始页
		int end = start + ARTICLES_PER_PAGE - 1; // 结束页

		Set<String> ids = conn.zrevrange(order, start, end);// 成员的位置按 score 值递减(从大到小)来排列
		List<Map<String, String>> articles = new ArrayList<Map<String, String>>();
		for (String id : ids) {
			// 根据ID查询文章对象
			Map<String, String> articleData = conn.hgetAll(id);
			articleData.put("id", id);
			articles.add(articleData);// 保存对象
		}

		return articles;// 返回对象集合
	}

	/**
	 * 文章加入群组
	 * @param conn
	 * @param articleId 文章ID
	 * @param toAdd 群组数组
	 * @date 2018年9月7日
	 */
	public void addGroups(Jedis conn, String articleId, String[] toAdd) {
		String article = "article:" + articleId;
		//群组 SET <group:group_name,ID>
		for (String group : toAdd) {
			conn.sadd("group:" + group, article);
		}
	}

	/**
	 * 根据score排序 分页查询文章
	 * @param conn
	 * @param group
	 * @param page
	 * @return
	 * @date 2018年9月7日
	 */
	public List<Map<String, String>> getGroupArticles(Jedis conn, String group, int page) {
		return getGroupArticles(conn, group, page, "score:");
	}

	/**
	 * 根据order排序 分页查询
	 * @param conn
	 * @param group
	 * @param page
	 * @param order
	 * @return
	 * @date 2018年9月7日
	 */
	public List<Map<String, String>> getGroupArticles(Jedis conn, String group, int page, String order) {
		String key = order + group;
		//不存在则进行zinterstore
		if (!conn.exists(key)) {
			ZParams params = new ZParams().aggregate(ZParams.Aggregate.MAX);
			//计算给定的一个或多个有序集的交集
			conn.zinterstore(key, params, "group:" + group, order);
			//过期超时删除
			conn.expire(key, 60);
		}
		return getArticles(conn, page, key);
	}

	/**
	 * 打印文章集合
	 * 
	 * @param articles
	 * @date 2018年9月6日
	 */
	private void printArticles(List<Map<String, String>> articles) {
		for (Map<String, String> article : articles) {
			System.out.println("  id: " + article.get("id"));
			for (Map.Entry<String, String> entry : article.entrySet()) {
				if (entry.getKey().equals("id")) {
					continue;
				}
				System.out.println("    " + entry.getKey() + ": " + entry.getValue());
			}
		}
	}
}
